/*
 * Copyright (c) 2003 TWINDIVIDIVISION SMPPEventHandler.java
 */
package guru.mercurio.eps.sSMPP;

import guru.mercurio.common.PhoneAddress;
import guru.merlin.core.ConfigurationEPException;
import guru.merlin.core.EPException;
//import guru.merlin.core.CantHandException_;
//import guru.merlin.core.EPExecutionException;
//import guru.merlin.core.EPInfrastructureException;
import guru.mercurio.eps.sSMPP.DataHandler;
import guru.merlin.core.PeerEPException;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Properties;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.smpp.Data;
import org.smpp.ServerPDUEvent;
import org.smpp.Session;
import org.smpp.TCPIPConnection;
import org.smpp.pdu.Address;
import org.smpp.pdu.AddressRange;
import org.smpp.pdu.BindReceiver;
import org.smpp.pdu.BindRequest;
import org.smpp.pdu.BindResponse;
import org.smpp.pdu.BindTransciever;
import org.smpp.pdu.BindTransmitter;
import org.smpp.pdu.CancelSM;
import org.smpp.pdu.CancelSMResp;
import org.smpp.pdu.DataSM;
import org.smpp.pdu.EnquireLink;
import org.smpp.pdu.EnquireLinkResp;
import org.smpp.pdu.PDU;
import org.smpp.pdu.QuerySM;
import org.smpp.pdu.QuerySMResp;
import org.smpp.pdu.Request;
import org.smpp.pdu.Response;
import org.smpp.pdu.SubmitMultiSM;
import org.smpp.pdu.SubmitMultiSMResp;
import org.smpp.pdu.SubmitSM;
import org.smpp.pdu.SubmitSMResp;
import org.smpp.pdu.UnbindResp;

/**
 * @author Santiago Ventura Infante Clase <code>SMPPOperator</code> Provee la implementacin de
 *         los mtodos definidos en la <br>
 *         interfaz SMPPConnector. <br>
 *         Bsicamente la implementacin de los mtodos envuelven las operaciones ms comunes
 *         especificadas <br>
 *         en el protocolo SMPP: <br>
 *         BIND <br>
 *         UNBIND <br>
 *         SUMIT_SM <br>
 *         SUBMIT_MULTI_SM <br>
 *         <br>
 *         Los mtodos implementados tambin incluyen aquellos esenciales para el establecimientos
 *         de la <br>
 *         comunicacin entre un ESME y SMSC, tales como la conexin TCP/IP y una sesin.. <br>
 *         Esta clase se vale de un archivo de propiedades para su funcionamiento, de lo contrario
 *         su <br>
 *         uso es imposible. El archivo de propiedades debe contener las claves y valores de <br>
 *         los parmetros <br>
 *         necesarios para cada una de las operaciones definidas por el protocolo SMPP. <br>
 * @version 0.1
 */
public class SMPPOperator implements SMPPConnector, Runnable{
    static Logger logger = Logger.getLogger(SMPPOperator.class);
    
    // ATRIBUTOS PARA LA CARGA DEL ARCHIVO DE PROPIEDADES
    private String smscId = null; // Identifica al operador ej. digitel,
    
    // telcel, movilnet, etc
    
    private String propertiesPath = null; // Indica la ruta donde se encuentra
    
    // el archivo de propiedades. Ej.
    // X:\\guru\\conf\\
    
    private String propertiesFile = null; // Nombre del archivo de
    
    //Objeto properties con las propiedades del protocolo smpp
    Properties properties = null;
    
    /* ATRIBUTOS PARA LAS OPERACIONES DEL SMPPOperator */
    SMPPEventHandler pduListener = null; /*
     * Instancia de la clase SMPPEventHandler que recibe todos
     * los PDUs recibidos desde el SMSC.
     */
    
    protected String nodeID; // ID del NODO
    
    private String ipAddress; // CONNECTION La direccin IP del SMSC
    
    private int port; // CONNECTION El nmero de puerto remoto del SMSC
    
    private TCPIPConnection connection = null; // CONNECTION Objeto connection,
    
    // que representa la conexin
    // con el SMSC
    
    private Session session = null; // CONNECTION Representa la comunicacin con
    
    // el SMSC
    
    private boolean autoReconnect = false; // CONNECTION Define la reconexin
    
    // automtica true/false
    
    private boolean connected = false; // CONNECTION Representa el status de la
    
    // conexin true (conectado) / false
    // (desconectado)
    
    private boolean bound = false; // BIND Representa el status del enlace true
    
    // (enlazado) / false (desenlazado)
    
    private BindRequest bindRequest = null; // BIND Representa la peticin de
    
    // enlace al SMSC
    
    private BindResponse bindResponse = null; // BIND Representa la respuesta
    
    // del SMSC a la peticin de
    // enlace
    
    private boolean asynchronous = false; // BIND Estaclece el status de la
    
    // comunicacin true (asynchronous)
    // / false (synchronous)
    
    private String systemId; // BIND Parmetro que identifica al ESME ante el
    
    // SMSC
    
    private String password; // BIND Parmetro usado por el SMSC para
    
    // autenticar al ESME
    
    private String systemType; // BIND Parmetro usado para categorizar al tipo
    
    // de ESME que esta enlazndose con el SMSC
    
    private byte interfaceVersion; // BIND Parmetro usado para indicar la
    
    // versin del protocolo SMPP
    
    private byte addrTon; // BIND Este campo define el tipo de nmero (Type of
    
    // Number) a ser usado en los parmetros de
    // direcciones SME.
    
    private byte addrNpi; // BIND Este campo define el Indicador de Plan
    
    // Numrico (Numeric Plan Indicator) a ser usado en
    // los parmetros de direcciones SME.
    
    private String addrRange; // BIND Cadema que representa al parmetro smpp,
    
    // address_range.
    
    private String bindType = "tr"; // BIND tipo de enlace (r/R: receiver, t/T:
    
    // transmitter, tr/TR: transceiver)
    
    private AddressRange addressRange = null; // BIND El objeto addressRange
    
    // es usado por los comandos
    // Bind Receiver y el Bind
    // Transceiver.
    
    private SubmitSM submitSMRequest = null; // SUBMIT Representa la peticin
    
    // de para el envo de un
    // mensaje corto desde el ESME
    // al SMSC
    
    private SubmitSMResp submitSMResponse = null; // SUBMIT Representa la
    
    // respuesta por parte del
    // SMSC a la peticin del
    // ESME para el envo de un
    // mensaje corto
    
    // private Address sourceAddress = null; //SUBMIT Representa la direccin
    // del ESME, desde donde se origino el mensaje
    // private Address destAddress = null; //SUBMIT Representa la direccin del
    // SME destino. Para una estacin mvil este es el nmero de directorio.
    private String serviceType; // SUBMIT Este parmetro sirve para indicar el
    
    // servicio asociado con el mensaje. WAP, VOICE
    // MAIL NOTIFICATION, etc.
    
    private byte sourceAddrTon; // SUBMIT Este campo define el tipo de nmero
    
    // (Type of Number) a ser usado en los
    // parmetros de direcciones SME.
    
    private byte sourceAddrNpi; // SUBMIT Este campo define el Indicador de Plan
    
    // Numrico (Numeric Plan Indicator) a ser usado
    // en los parmetros de direcciones SME.
    
    // private String srcAddr; //SUBMIT Esta cadena representa la direccin del
    // ESME desde donde se origino el mensaje
    private byte destAddrTon; // SUBMIT Este campo define el tipo de nmero
    
    // (Type of Number) a ser usado en los
    // parmetros de direcciones SME.
    
    private byte destAddrNpi; // SUBMIT Este campo define el Indicador de Plan
    
    // Numrico (Numeric Plan Indicator) a ser usado
    // en los parmetros de direcciones SME.
    
    private byte esmClass; // SUBMIT Este parmetro es usado para indicar un
    
    // mensaje especial asociado con el SM.
    
    private byte protocolId; // SUBMIT Identifica al protocolo. GSM. TDMA,
    
    // CDMA, etc.
    
    private byte priorityFlag; // SUBMIT Asigna el nivel de prioridad (0-3).
    
    // Siendo 3 la de mayor prioridad.
    
    private String scheduleDeliveryTime; // SUBMIT Este parmetro especifica
    
    // cuando el mensaje debe ser
    // entregado por el SMSC.
    
    private String validityPeriod; // SUBMIT Este parmetro le indica al SMSC
    
    // cuando el mensaje debe ser descartado, si
    // no es entregado a su destinatario.
    
    private byte registeredDelivery; // SUBMIT Este parmetro es usado para
    
    // pedirle al SMSC un recibo de entrega
    // del mensaje
    
    private byte replaceIfPresentFlag; // SUBMIT Este parmetro es usado para
    
    // pedirle al SMSC que reemplace un
    // mensaje enviado previamente.
    
    private byte dataCoding; // SUBMIT Indica al SMSC como los datos deben
    
    // ser codificados.
    
    private byte smDefaultMsgId; // SUBMIT Indica al SMSC el indice para un
    
    // mensaje predefinido.
    
    private short smLength; // SUBMIT Este parmetro es usado para indicar la
    
    // longitud de el short_message.
    
    private String shortMessage; // SUBMIT Representa al mensaje corto.
    
    private String messageId; // SUBMIT Representa el identificador nico del
    
    // mensaje, asignado por el SMSC a cada mensaje
    // corto enviado.
    
    private int intTimeout = 30; // RECEIVE Tiempo de espera (segundos) por
    
    // la recepcin de mensajes. Debe ser
    // convertido a milisegundos.
    
    private SubmitMultiSM subMultiRequest = null;
    
    // SUBMIT MULTI Representa la peticin de para el envo de un mensaje corto
    // desde el ESME al SMSC.Este mensaje ser entregado a multiples
    // destinatarios.
    private SubmitMultiSMResp subMultiResponse = null;
    
    // SUBMIT MULTI Representa la respuesta por parte del SMSC a la peticin del
    // ESME para el envo de un mensaje corto. Este mensaje ser entregado a
    // multiples destinatarios.
    private Vector destAddresses = null; // Contiene las direcciones de
    
    // destino donde el SMSC debe
    // entregar el mensaje. Es usada
    // cuando el mensaje debe ser
    // entregado a multiples
    // destinatarios.
    
    private DataSM dataSMrequest = null; // DATA SM
    
    // LOS SIGUIENTES ATRIBUTOS DE TIPO CADENA CONTENIEN EL RESULTADO DE
    // LA EJECUCION DE LOS METODOS IMPLEMENTADOS DE LA INTERFAZ SMPPConnector
    
    private String openConnectionResult; // Contiene el resultado de la
    
    // operacin openConnection()
    
    private String closeConnectionResult; // Contiene el resultado de la
    
    // operacin closeConnection()
    
    private String getBindResult; // Contiene el resultado de la operacin
    
    // getBind()
    
    private String closeBindResult; // Contiene el resultado de la operacin
    
    // closeBind()
    
    private String stopReceiveSMSResult; // Contiene el resultado de la
    
    private long enquireLinkTime;
    
    // operacin stopReceiveSMS()
    
    private Thread toReceiver;
    
    private guru.mercurio.eps.sSMPP.DataHandler dataHandler;
    
    private ReceivedMessageHandler messageHandler;
    
    private boolean keepReceiving = true;
    
    /**
     * Constructor sin argumentos. Los atributos smscId, propertiesPath y propertiesFile deben ser
     * asignados al objetos SMPPOperator, para poder cargar el archivo de propiedades.
     */
    public SMPPOperator() {
        this.setToReceiver(new Thread(this));
        // receivedMessageStore = new ReceivedMessageStore();
    }
    
    /**
     * Constructor con argumentos
     *
     * @param smsc
     *            Identifica al operador (SMSC) ej. digitel, telcel, movilnet, etc
     * @param pPath
     *            La ruta donde se encuentra el archivo de propiedades
     * @param pFile
     *            El nombre del archivo de propiedades
     * @param DataHandler
     *            handler
     */
    public SMPPOperator(String smsc, String pPath, String pFile, DataHandler handler) throws IOException {
        setSmscId(smsc);
        setPropertiesPath(pPath);
        setPropertiesFile(pFile);
        loadProperties();
        this.setToReceiver(new Thread(this));
        dataHandler = handler;
    }
    
    // IMPLEMENTACION DE LOS METODOS DE LA INTERFAZ SMPPConnector
    
    /**
     * Establece la conexin va TCP/IP con el SMSC
     *
     * @param smscIpAddress
     *            Direccin IP del SMSC
     * @param smscPort
     *            Puerto de conexin del SMSC
     * @return 1 si se ha establecido la conexin, 2 si la conexin ya existe, y 0 cualquier otra
     *         cosa
     */
    public int openConnection(String smscIpAddress, int smscPort) throws EPException {
        int successfull = 0;
        String msg;
        if (connection == null || this.isConnected() == false) {
            try {
                Socket socket = new Socket(InetAddress.getByName(smscIpAddress), smscPort);
                connection = new TCPIPConnection(socket);
                connection.setCommsTimeout(0);
                connection.setReceiveTimeout(500);
                session = new Session(connection);
                this.setConnected(true);
                messageHandler = new ReceivedMessageHandler(this.dataHandler, this.session, this.properties);
                successfull = 1;
                
                if (logger.isDebugEnabled()) {
                    msg = "Connection TCP/IP has been established successfully. " + smscIpAddress + " " + smscPort;
                    logger.debug(msg);
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new PeerEPException(e.toString());
            }
        } else if (this.isConnected()) {
            successfull = 2;
            if (logger.isDebugEnabled()) {
                logger.debug("Connection TCP/IP already had been established previously.");
            }
        }
        return successfull;
    }
    
    /**
     * Establece la conexin va TCP/IP con el SMSC
     *
     * @return 1 si se ha establecido la conexin, 2 si la conexin ya existe, y 0 cualquier otra
     *         cosa
     */
    public int openConnection() throws EPException {
        int successfull = 0;
        try {
            successfull = openConnection(this.ipAddress, this.port);
            messageHandler = new ReceivedMessageHandler(this.dataHandler, this.session, this.properties);
        } catch (Exception e) {
            throw new PeerEPException(e.getMessage());
        }
        return successfull;
    }
    
    /**
     * Cierra la conexin con el SMSC
     */
    public void closeConnection() {
        String msg = "";
        if (connection != null) {
            try {
                session.close();
                this.setConnected(false);
                if (logger.isDebugEnabled()) {
                    msg = "Connection TCP/IP has been closed successfully.";
                    logger.debug(msg);
                }
            } catch (IOException ioe) {
                ioe.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            if (logger.isDebugEnabled()) {
                msg = "Connection TCP/IP already has been closed previously.";
                logger.debug(msg);
            }
        }
    }
    
    /**
     * Establece el enlace con el SMSC
     *
     * @return 1 si se ha establecido el enlace con el SMSC, 2 si el enlace ya existe, y 0 cualquier
     *         otra cosa
     */
    public int getBind() throws EPException {
        int successfull = 0;
        if (session.isBound()) {
            successfull = 2;
            return successfull;
        }
        
        if (session != null) {
            String bindT = this.getBindType();
            try {
                // Se establece el enlace, segn el valor que tenga el parmetro bindType
                if (bindT.equalsIgnoreCase("t")) {
                    bindRequest = new BindTransmitter();
                } else if (bindT.equalsIgnoreCase("r")) {
                    bindRequest = new BindReceiver();
                } else if (bindT.equalsIgnoreCase("tr")) {
                    bindRequest = new BindTransciever();
                } else {
                    logger.error("The type of bind to the smsc is not an option legal.");
                    return successfull;
                }
                
                // Se asigna los parmetros obligatorios para el enlace.
                bindRequest.setSystemId(this.systemId);
                bindRequest.setPassword(this.password);
                bindRequest.setSystemType(this.systemType);
                bindRequest.setInterfaceVersion(interfaceVersion);
                addressRange = new AddressRange(addrTon, addrNpi, addrRange);
                bindRequest.setAddressRange(addressRange);
                
                // Se efectua el enlace
                
                if (asynchronous) {
                    pduListener = new SMPPEventHandler(session);
                    bindResponse = session.bind(bindRequest, pduListener);
                } else {
                    bindResponse = session.bind(bindRequest);
                }
                // Si el enlace es exitoso hacemos true a la propiedad bound
                if (session.isBound()) {
                    this.setBound(true);
                    successfull = 1;
                }
            } catch (org.smpp.pdu.WrongLengthOfStringException wlose) {
                wlose.printStackTrace();
                throw new ConfigurationEPException(wlose.toString());
            } catch (org.smpp.pdu.ValueNotSetException vnse) {
                vnse.printStackTrace();
                throw new ConfigurationEPException(vnse.toString());
            } catch (Exception e) {
                throw new PeerEPException(e.toString());
            }
        }
        return successfull;
    }
    
    /**
     * Cierra el enlace, pero la conexin al SMSC permanece abierta. Esta operacin puede tomar
     * algn tiempo si el enlace es asynchronous.
     */
    public void closeBind() {
        UnbindResp unbindResponse = null;
        if (session != null) {
            try {
                if (session.isBound()) {
                    // Cerrar el enlace
                    unbindResponse = this.session.unbind();
                    messageHandler.processSMSCResponse(unbindResponse);
                    this.setBound(false);
                }
            } catch (org.smpp.pdu.ValueNotSetException vnse) {
                vnse.printStackTrace();
            } catch (org.smpp.TimeoutException te) {
                te.printStackTrace();
            } catch (org.smpp.pdu.PDUException pdue) {
                pdue.printStackTrace();
            } catch (IOException ioe) {
                ioe.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    private boolean isANumber(String number) {
        try {
            new Long(number);
            return (true);
        } catch (Exception e) {
            return (false);
        }
    }
    
    /**
     * Envia un mensaje al SMSC. El tipo de enlace debe ser como transmitter/transceiver
     *
     * @param destAddr
     *            Numero del destinatario del mensaje
     * @param shortMessage
     *            Mensaje corto que ser enviado al destinatario.
     * @return true si se ha enviado el mensaje al SMSC, false cualquier otra cosa
     */
    public void submitSMS(PhoneAddress destPhone, String shortMessage, String shortCode, int sequence) throws EPException {
        String msg = "";
        //poner el 139
        submitSMRequest = new SubmitSM();
        if (destPhone.getAreaCode().equals("416") && destAddrTon != 1) {
            shortCode = "139" + shortCode;
        }
        if (logger.isDebugEnabled()) {
            msg = "submitSMS request received for destPhone='" + destPhone + "', msg='" + shortMessage + "', shortCode='" + shortCode.trim() + "'";
            logger.debug(msg);
        }
        
        msg = "submitSMS request received for destPhone='" + destPhone + "', msg='" + shortMessage + "', shortCode='" + shortCode.trim() + "'";
        logger.info(msg);
        
        try {
            String str = "";
            
            switch (destAddrTon) {
                case 1:
                    switch (destAddrNpi) {
                        case 1://DIGITEL
                            if ((destPhone.getLocal()).length() < 7) {
                                while ((destPhone.getLocal()).length() < 7) {
                                    destPhone.setLocal("0" + destPhone.getLocal());
                                }
                            }
                            //str = "0" + destPhone.getAreaCode() + destPhone.getLocal();
                            str = destPhone.getCountryCode() + destPhone.getAreaCode() + destPhone.getLocal();
                            break;
                        case 9:
                            if ((destPhone.getLocal()).length() < 7) {
                                while ((destPhone.getLocal()).length() < 7) {
                                    destPhone.setLocal("0" + destPhone.getLocal());
                                }
                            }
                            
                            String narear = destPhone.getAreaCode();
                            if (narear.equals("412")) {//Digitel (03458412xxxxxxx)
                                str = "03458412" + destPhone.getLocal();
                            } else if (narear.equals("414")) {//Movistar (0270414xxxxxxx)
                                str = "0270414" + destPhone.getLocal();
                            } else if (narear.equals("416")) {//Movilnet (004xxxxxxx)
                                str = "004" + destPhone.getLocal();
                            } else if (narear.equals("418")) {//Infonet (04958418xxxxxxx)
                                str = "04958418" + destPhone.getLocal();
                            }
                            break;
                        default:
                            throw new ConfigurationEPException("Dont know destAddrNpi");
                    }
                    break;
                case 2:
                    if ((destPhone.getLocal()).length() < 7) {
                        while ((destPhone.getLocal()).length() < 7) {
                            destPhone.setLocal("0" + destPhone.getLocal());
                        }
                    }
                    if (destPhone.getAreaCode().equals("416")) {
                        str = "158" + destPhone.getLocal();
                    } else {
                        str = "0" + destPhone.getAreaCode() + destPhone.getLocal();
                    }
                    break;
                default:
                    throw new ConfigurationEPException("Dont know destAddrNpi");
            }
            // Se asigna los parmetros obligatorios para el envo del mensaje.
            submitSMRequest.setServiceType(serviceType);
            
            Address destAddress = new Address(destAddrTon, destAddrNpi, str);
            //logger.info("Destination Address=" + destAddress.toString());
            submitSMRequest.setDestAddr(destAddress);
            
            Address sourceAddress;
            if (isANumber(shortCode)) {
                sourceAddress = new Address(sourceAddrTon, sourceAddrNpi, shortCode);
            } else {
                sourceAddress = new Address(Byte.parseByte("5"), sourceAddrNpi, shortCode);
            }
            submitSMRequest.setSourceAddr(sourceAddress);
            
            
            submitSMRequest.setEsmClass(esmClass);
            submitSMRequest.setProtocolId(protocolId);
            submitSMRequest.setPriorityFlag(priorityFlag);
            submitSMRequest.setScheduleDeliveryTime(scheduleDeliveryTime);
            submitSMRequest.setValidityPeriod(validityPeriod);
            
            submitSMRequest.setRegisteredDelivery(registeredDelivery);
            
            submitSMRequest.setReplaceIfPresentFlag(replaceIfPresentFlag);
            submitSMRequest.setDataCoding(dataCoding);
            submitSMRequest.setSmDefaultMsgId(smDefaultMsgId);
            // submitSMRequest.setSmLength(smLength);
            submitSMRequest.setShortMessage(shortMessage);
            submitSMRequest.setAlertOnMsgDelivery(false);
            
            // setMessageId("");
            
            if (session.isBound()) {
                // Se efectua el envo del mensaje
                // submitSMRequest.assignSequenceNumber(true);
                submitSMRequest.setSequenceNumber(sequence);
                
                session.submit(submitSMRequest); // ojo, esto devuelve la
                // respuesta su estamos en
                // sincrono.
                
                if (!asynchronous) {
                    // messageHandler.SMSCResponse(response); donde response es
                    // lo que retorna el submit anterior.
                    throw new PeerEPException("No se como manejar una comunicacin sncrona");
                }
            } else {
                throw new PeerEPException("SMPPEP is not bound with the smsc");
            }
        } catch (org.smpp.pdu.ValueNotSetException vnse) {
            throw new ConfigurationEPException(vnse.toString());
        } catch (org.smpp.pdu.WrongLengthOfStringException wlose) {
            throw new ConfigurationEPException(wlose.toString());
        } catch (org.smpp.pdu.WrongDateFormatException wdfe) {
            throw new ConfigurationEPException(wdfe.toString());
        } catch (org.smpp.TimeoutException te) {
            throw new PeerEPException(te.toString());
        } catch (org.smpp.pdu.PDUException pdue) {
            throw new PeerEPException(pdue.toString());
        } catch (IOException ioe) {
            throw new PeerEPException(ioe.toString());
        } catch (org.smpp.WrongSessionStateException wsse) {
            throw new PeerEPException(wsse.toString());
        } catch (NullPointerException e){
            throw new PeerEPException(e.toString());
        }
    }
    
    /**
     * Envia multiples mensajes al SMSC. El tipo de enlace debe ser como transmitter/transceiver
     *
     * @param destAddresses
     *            Direcciones de los destinatarios del mensaje
     * @param shortMessage
     *            Mensaje corto que ser enviado a todos los destinatario.
     * @return true si se ha enviado el mensaje al SMSC, false cualquier otra cosa
     */
    /*
     * public boolean submitMultiSMS(Vector destAddresses, String shortMessage) { boolean
     * successfull = false; subMultiRequest = new SubmitMultiSM(); Response response; try { // Se
     * asigna los parmetros obligatorios para el envo del mensaje.
     * subMultiRequest.setServiceType(serviceType); sourceAddress = new Address(sourceAddrTon,
     * sourceAddrNpi, srcAddr); subMultiRequest.setSourceAddr(sourceAddress); // Asignamos las
     * direcciones de destino del mensaje this.destAddresses = destAddresses; Iterator numOfDests =
     * destAddresses.iterator(); int elements = 0; while (elements < destAddresses.size()) {
     * subMultiRequest.addDestAddress(new DestinationAddress(destAddrTon, destAddrNpi, (String)
     * destAddresses.get(elements))); elements++; } subMultiRequest.setEsmClass(esmClass);
     * subMultiRequest.setProtocolId(protocolId); subMultiRequest.setPriorityFlag(priorityFlag);
     * subMultiRequest.setScheduleDeliveryTime(scheduleDeliveryTime);
     * subMultiRequest.setValidityPeriod(validityPeriod);
     * subMultiRequest.setRegisteredDelivery(registeredDelivery);
     * subMultiRequest.setReplaceIfPresentFlag(replaceIfPresentFlag);
     * subMultiRequest.setDataCoding(dataCoding); subMultiRequest.setSmDefaultMsgId(smDefaultMsgId);
     * //submitSMRequest.setSmLength(smLength); subMultiRequest.setShortMessage(shortMessage);
     * setMessageId(""); if (session.isBound()) { // Se efectua el envo del mensaje if
     * (asynchronous) { session.submitMulti(subMultiRequest); successfull = true; } else { response =
     * session.submitMulti(subMultiRequest); messageHandler.SMSCResponse(response); successfull =
     * true; } } } catch (org.smpp.pdu.ValueNotSetException vnse) { vnse.printStackTrace(); } catch
     * (org.smpp.pdu.WrongLengthOfStringException wlose) { wlose.printStackTrace(); } catch
     * (org.smpp.pdu.WrongDateFormatException wdfe) { wdfe.printStackTrace(); } catch
     * (org.smpp.TimeoutException te) { te.printStackTrace(); } catch (org.smpp.pdu.PDUException
     * pdue) { pdue.printStackTrace(); } catch (IOException ioe) { ioe.printStackTrace(); } catch
     * (org.smpp.WrongSessionStateException wsse) { wsse.printStackTrace(); } catch (Exception e) {
     * e.printStackTrace(); } return successfull; }
     */
    
    /*
     * public int submitAndDeliverDataSM(String destAddr, String shortMessage) { int successfull =
     * 0; dataSMrequest = new DataSM(); DataSMResp response; try { // Se asigna los parmetros
     * obligatorios dataSMrequest.setServiceType(serviceType);
     * dataSMrequest.setSourceAddr(sourceAddress); dataSMrequest.setDestAddr(destAddress);
     * dataSMrequest.setEsmClass(esmClass); dataSMrequest.setRegisteredDelivery(registeredDelivery);
     * dataSMrequest.setDataCoding(dataCoding); // send the request //System.out.println("Data
     * request " + dataSMrequest.debugString()); if (asynchronous) { session.data(dataSMrequest); }
     * else { response = session.data(dataSMrequest); //System.out.println("Data response " +
     * response.debugString()); messageId = response.getMessageId(); } } catch
     * (org.smpp.pdu.ValueNotSetException vnse) { vnse.printStackTrace(); } catch
     * (org.smpp.pdu.WrongLengthOfStringException wlose) { wlose.printStackTrace(); } catch
     * (org.smpp.pdu.WrongDateFormatException wdfe) { wdfe.printStackTrace(); } catch
     * (org.smpp.TimeoutException te) { te.printStackTrace(); } catch (org.smpp.pdu.PDUException
     * pdue) { pdue.printStackTrace(); } catch (IOException ioe) { ioe.printStackTrace(); } catch
     * (org.smpp.WrongSessionStateException wsse) { wsse.printStackTrace(); } catch (Exception e) {
     * e.printStackTrace(); } return successfull; }
     */
    /**
     * Inicia el hilo responsable de invocar al mtodo receiveDeliverSM(). El mtodo
     * receiveDeliverSM() es el encargado de recibir y procesar los mensajes del tipo DeliverSM del
     * SMSC. El tipo de enlace debe ser como receiver/transceiver.
     *
     * @see #run()
     */
    public void receiveSMS() throws Exception {
        try {
            if (this.getToReceiver() == null) {
                this.setKeepReceiving(true);
                this.setToReceiver(new Thread(this));
                this.getToReceiver().setDaemon(true);
                this.getToReceiver().start();
                // this.getToReceiver().run();
            } else {
                if (this.getToReceiver().isAlive()) {
                    this.setKeepReceiving(true);
                } else {
                    this.setKeepReceiving(true);
                    this.getToReceiver().setDaemon(true);
                    this.getToReceiver().start();
                }
            }
        } catch (IllegalThreadStateException ie) {
            ie.printStackTrace();
            logger.error("", ie);
            throw ie;
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("", e);
            throw e;
        }
    }
    
    /**
     * Mtodo cancelSM. Este mtodo es invocado para cancelar uno o ms mensajes cortos enviados
     * previamente, <br>
     * y que todava no han sido entregados al destinatario. <br>
     *
     * @param messId
     *            Identificador del mensajes a ser cancelado. Este debe ser el message_id asignado
     *            <br>
     *            por el SMSC al mensaje original <br>
     * @param sourAddre
     *            Source Address (direccin de origen) del mensaje a ser cancelado.
     * @param destAddre
     * @param servType
     * @return boolean
     */
    public void cancelSM(String messId, String sourAddre, String destAddre, String servType) {
        boolean cancelado = false;
        
        CancelSM cancelSM = new CancelSM();
        CancelSMResp cancelResponse;
        try {
            cancelSM.setMessageId(messId);
            cancelSM.setSourceAddr(sourAddre);
            cancelSM.setDestAddr(destAddre);
            cancelSM.setServiceType(servType);
            
            if (asynchronous) {
                session.cancel(cancelSM);
            } else {
                cancelResponse = session.cancel(cancelSM);
                if (cancelResponse.isOk()) {
                    
                }
            }
        } catch (org.smpp.pdu.ValueNotSetException vnse) {
            vnse.printStackTrace();
        } catch (org.smpp.pdu.WrongLengthOfStringException wlose) {
            wlose.printStackTrace();
        } catch (org.smpp.pdu.WrongDateFormatException wdfe) {
            wdfe.printStackTrace();
        } catch (org.smpp.TimeoutException te) {
            te.printStackTrace();
        } catch (org.smpp.pdu.PDUException pdue) {
            pdue.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (org.smpp.WrongSessionStateException wsse) {
            wsse.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void querySM(String messId, String sourAddre, String destAddre, String servType) {
        QuerySM querySM = new QuerySM();
        QuerySMResp queryResponse;
        try {
            querySM.setMessageId(messId);
            querySM.setSourceAddr(sourAddre);
            
            if (asynchronous) {
                session.query(querySM);
            } else {
                queryResponse = session.query(querySM);
                if (queryResponse.isOk()) {
                    queryResponse.getMessageState();
                }
            }
        } catch (org.smpp.pdu.ValueNotSetException vnse) {
            vnse.printStackTrace();
        } catch (org.smpp.pdu.WrongLengthOfStringException wlose) {
            wlose.printStackTrace();
        } catch (org.smpp.pdu.WrongDateFormatException wdfe) {
            wdfe.printStackTrace();
        } catch (org.smpp.TimeoutException te) {
            te.printStackTrace();
        } catch (org.smpp.pdu.PDUException pdue) {
            pdue.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (org.smpp.WrongSessionStateException wsse) {
            wsse.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * Detiene la recepcin de mensajes.
     *
     * @see #run()
     */
    
    public void stopReceiveSMS() {
        if (this.getToReceiver().isAlive()) {
            //this.toReceiver = null; --->Comentado 23/08/2006 para modificar el stop del nodo
            this.setKeepReceiving(false);
            logger.info("keep receiving "+false);
        }
    }
    
    /**
     * Establece el status de la Autoreconexin
     *
     * @param status
     *            true (reconectar) / false (no reconectar).
     */
    public void setAutoReconnect(boolean status) {
        autoReconnect = status;
    }
    
    /**
     * Devuelve el status de la autoreconexin.
     *
     * @return true (reconectar) / false (no reconectar).
     */
    public boolean getAutoReconnect() {
        return autoReconnect;
    }
    
    // FIN DE LA IMPLEMENTACION DE LOS METODOS DE LA INTERFAZ SMPPConnector
    
    /**
     * Esta mtodo es el encargado de recibir los PDUs (mensajes) provenientes del SMSC. Este mtodo
     * determina la forma del enlace (synchronous/asynchronous) y establece el tiempo de timeout
     * para la recepcin de mensajes en 500 milisegundos. Es llamado por el Thread de "recepcin"
     * cada un intervalo de tiempo. Siempre que un PDU es recibido, determina si es del tipo Request
     * o Response y hace la llamada al mtodo guru.merlin.connectors.MessageHandler.SMSCRequest()
     * guru.merlin.connectors.MessageHandler.SMSCResponse(), segn corresponda.
     */
    public void receiveDeliverSM() {
        
        Response response;
        try {
            PDU pdu = null;
            long longTimeout = 500;
            
            // Se recibe el mensaje
            if (asynchronous) {
                ServerPDUEvent pduReqEvent = pduListener.getRequestEvent(longTimeout);
                if (pduReqEvent != null) {
                    pdu = pduReqEvent.getPDU();
                }
                
                ServerPDUEvent pduRespEvent = pduListener.getResponseEvent(longTimeout);
                if (pduRespEvent != null) {
                    pdu = pduRespEvent.getPDU();
                }
            } else {
                pdu = session.receive(longTimeout);
            }
            
            if (pdu != null) {
                if (pdu.isRequest()) {
                    messageHandler.processSMSCRequest((Request) pdu);
                }
                if (pdu.isResponse()) {
                    messageHandler.processSMSCResponse((Response) pdu);
                }
            }
        } catch (org.smpp.pdu.ValueNotSetException vnse) {
            vnse.printStackTrace();
        } catch (org.smpp.pdu.WrongLengthOfStringException wlose) {
            wlose.printStackTrace();
        } catch (org.smpp.pdu.WrongDateFormatException wdfe) {
            wdfe.printStackTrace();
        } catch (org.smpp.TimeoutException te) {
            te.printStackTrace();
        } catch (org.smpp.pdu.PDUException pdue) {
            pdue.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /*
     * private ReceivedMessage buildMessage(org.smpp.pdu.DeliverSM deliverSM) { ReceivedMessage
     * rMessage = null; DateFormat formatter =
     * DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.MEDIUM,Locale.FRANCE); DeliverSM
     * deliverSMRequest = deliverSM; if (deliverSMRequest!=null) { rMessage = new ReceivedMessage();
     * Date time = new Date(); rMessage.setDelivery_Time(formatter.format(time)); String dadd =
     * deliverSMRequest.getDestAddr().debugString(); rMessage.setDest_Address(dadd); String status =
     * String.valueOf(deliverSMRequest.getCommandStatus()); rMessage.setDelivery_Status(status);
     * String msg = deliverSMRequest.getShortMessage(); rMessage.setShort_Message(msg); String sadd =
     * deliverSMRequest.getSourceAddr().debugString(); rMessage.setSrc_Address(sadd); } return
     * rMessage; }
     */
    
    /**
     * Esta mtodo es el encargado de enviar Enquire Links al SMSC. La idea de los Enquire Links es
     * verificar el status de la comunicacin, adems de mantener viva la conexin con el SMSC.
     */
    public void toKeepAlive() {
        try {
            EnquireLink request = new EnquireLink();
            EnquireLinkResp response;
            if (asynchronous) {
                session.enquireLink(request);
            } else {
                response = session.enquireLink(request);
                messageHandler.processSMSCResponse(response);
            }
        } catch (Exception e) {
            //logger.error(e.getMessage());
        }
    }
    
    /**
     * Implementacin del mtodo run() de la interfaz Runnable. Se mantiene haciendo llamadas al
     * mtodo receiveDeliverSM() mientras el valor de la propiedad keepReceiving sea igual a true o
     * hasta que el mtodo stopReceiveSMS() sea llamado. Cada vez que el mtodo receiveDeliverSM()
     * es llamado el hilo es puesto a dormir durante 30 milisegundos y el mtodo receiveDeliverSM()
     * vuelve a ser invocado.
     */
    public void run() {
        if (nodeID != null) {
            Thread.currentThread().setName(nodeID);
        }
        
        try {
            long tiempo = 0;
            while (this.keepReceiving) {
                this.receiveDeliverSM();
                tiempo = 30;
                Thread.sleep(tiempo);
            }
            if (!this.keepReceiving) {
                logger.warn("The reception of messages has stopped.");
            }
        } catch (InterruptedException ie) {
            // ie.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * @param smsc
     *            identifica al operador (SMSC) ej. digitel, telcel, movilnet, etc
     */
    public void setSmscId(String smsc) {
        this.smscId = smsc;
    }
    
    /**
     * @return identificador del operador (SMSC) ej. digitel, telcel, movilnet, etc
     */
    public String getSmscId() {
        return smscId;
    }
    
    /**
     * @param pPath
     *            la ruta donde se encuentra el archivo de propiedades
     */
    public void setPropertiesPath(String pPath) {
        this.propertiesPath = pPath;
    }
    
    /**
     * @return la ruta donde se encuentra el archivo de propiedades
     */
    public String getPropertiesPath() {
        return propertiesPath;
    }
    
    /**
     * @param pFile
     *            El nombre del archivo de propiedades
     */
    public void setPropertiesFile(String pFile) {
        this.propertiesFile = pFile;
    }
    
    /**
     * @return El nombre del archivo de propiedades
     */
    public String getPropertiesFile() {
        return propertiesFile;
    }
    
    /**
     * Devuelve el status para el enlace con el SMSC.
     *
     * @return true (enlazado) / false (desenlazado).
     */
    public boolean isBound() {
        return bound;
    }
    
    /**
     * Asigna el status para el enlace con el SMSC.
     *
     * @param bound
     *            true (enlazado) / false (desenlazado).
     */
    private void setBound(boolean bound) {
        this.bound = bound;
    }
    
    /**
     * Devuelve el status para la conexin TCP/IP
     *
     * @return true (conectado) / false (desconectado).
     */
    public boolean isConnected() {
        return connected;
    }
    
    /**
     * Asigna el status para la conexin TCP/IP
     *
     * @param connected
     *            true (conectado) / false (desconectado).
     */
    private void setConnected(boolean connected) {
        this.connected = connected;
    }
    
    /**
     * Devuelve el valor del atributo asynchronous.
     *
     * @return true (asynchronous) / false (synchronous).
     */
    public boolean isAsynchronous() {
        return asynchronous;
    }
    
    /**
     * Asigna el valor del atributo asynchronous.
     *
     * @param asynchronous
     *            true (asynchronous) / false (synchronous).
     */
    private void setAsynchronous(boolean asynchronous) {
        this.asynchronous = asynchronous;
    }
    
    /**
     * El valor de este atributo es asignado por el SMSC a cada mensaje corto enviado.
     *
     * @return una cadena con el valor del atributo messageId.
     */
    public String getMessageId() {
        return messageId;
    }
    
    /**
     * Establece un nuevo valor para el atributo messageId. El valor de este atributo es asignado
     * por el SMSC a cada mensaje corto enviado.
     *
     * @param messageId
     */
    private void setMessageId(String messageId) {
        this.messageId = messageId;
    }
    
    /**
     * Carga el archivo de propiedades necesario para el funcionamiento de las operaciones smpp.
     */
    public void loadProperties() {
        try {
            String path = getPropertiesPath();
            String file = getPropertiesFile();
            String prop = path + file;
            this.properties = new Properties();
            InputStream fis = new FileInputStream(prop);
            properties.load(fis);
            fis.close();
            
            nodeID = properties.getProperty("nodeID").trim();
            
            // CONNECTIONS
            ipAddress = properties.getProperty(getSmscId() + "_ip_address").trim();
            port = Integer.parseInt(properties.getProperty(getSmscId() + "_port").trim());
            
            // BIND
            systemId = properties.getProperty(getSmscId() + "_system_id").trim();
            password = properties.getProperty(getSmscId() + "_password").trim();
            systemType = properties.getProperty(getSmscId() + "_system_type").trim();
            addrTon = Byte.parseByte(properties.getProperty(getSmscId() + "_addr_ton").trim());
            addrNpi = Byte.parseByte(properties.getProperty(getSmscId() + "_addr_npi").trim());
            addrRange = properties.getProperty(getSmscId() + "_address_range", Data.DFLT_ADDR_RANGE);
            String intVersion = properties.getProperty(getSmscId() + "_interface_version");
            if (intVersion.equals("0x33")) {
                interfaceVersion = (byte) 0x33;
            } else if (intVersion.equals("0x50")) {
                interfaceVersion = (byte) 0x50;
            } else {
                interfaceVersion = (byte) 0x34;
            }
            
            String asynch = properties.getProperty(getSmscId() + "_asynchronous").trim();
            
            if (asynch.equalsIgnoreCase("true")) {
                setAsynchronous(true);
            } else if (asynch.equalsIgnoreCase("false")) {
                setAsynchronous(false);
            }
            
            String bindT = properties.getProperty(getSmscId() + "_bindType").trim();
            this.setBindType(bindT);
            
            // SUBMIT
            serviceType = properties.getProperty(getSmscId() + "_service_type");
            sourceAddrTon = Byte.parseByte(properties.getProperty(getSmscId() + "_source_addr_ton"));
            sourceAddrNpi = Byte.parseByte(properties.getProperty(getSmscId() + "_source_addr_npi"));
            // srcAddr = properties.getProperty(getSmscId() + "_source_addr");
            destAddrTon = Byte.parseByte(properties.getProperty(getSmscId() + "_dest_addr_ton"));
            destAddrNpi = Byte.parseByte(properties.getProperty(getSmscId() + "_dest_addr_npi"));
            esmClass = Byte.parseByte(properties.getProperty(getSmscId() + "_esm_class"));
            protocolId = Byte.parseByte(properties.getProperty(getSmscId() + "_protocol_id"));
            priorityFlag = Byte.parseByte(properties.getProperty(getSmscId() + "_priority_flag"));
            scheduleDeliveryTime = properties.getProperty(getSmscId() + "_schedule_delivery_time");
            validityPeriod = properties.getProperty(getSmscId() + "_validity_period");
            registeredDelivery = Byte.parseByte(properties.getProperty(getSmscId() + "_registered_delivery"));
            replaceIfPresentFlag = Byte.parseByte(properties.getProperty(getSmscId() + "_replace_if_present_flag"));
            dataCoding = Byte.parseByte(properties.getProperty(getSmscId() + "_data_coding"));
            smDefaultMsgId = Byte.parseByte(properties.getProperty(getSmscId() + "_sm_default_msg_id"));
            // smLength =
            // Integer.parseInt(properties.getProperty(getSmscId()+"_sm_length"));
            shortMessage = properties.getProperty(getSmscId() + "_short_message");
            
            // RECEIVE
            intTimeout = Integer.parseInt(properties.getProperty(getSmscId() + "_receive_timeout"));
            
            String enquireLinkT = properties.getProperty("enquire_link_time").trim();
            enquireLinkTime = Long.parseLong(enquireLinkT);
            
            driver = properties.getProperty("mercurio.db.driver");
            dbmsURL = properties.getProperty("mercurio.db.dbmsURL");
            username = properties.getProperty("mercurio.db.username");
            passworddb = properties.getProperty("mercurio.db.password");
        } catch (IOException ioe) {
            // System.out.println("Ha ocurrido una IOException durante la carga
            // del archivo de propiedades.");
            // System.out.println("loadProperties()");
            ioe.printStackTrace();
        } catch (NumberFormatException nfe) {
            // System.out.println("Ha ocurrido una NumberFormatException durante
            // la carga del archivo de propiedades.");
            // System.out.println("loadProperties()");
            nfe.printStackTrace();
        } catch (Exception exc) {
            // System.out.println("Ha ocurrido una NumberFormatException durante
            // la carga del archivo de propiedades.");
            // System.out.println("loadProperties()");
            exc.printStackTrace();
        }
    }
    
    /**
     * Getter for property toReceiver.
     *
     * @return Value of property toReceiver.
     */
    public java.lang.Thread getToReceiver() {
        return toReceiver;
    }
    
    /**
     * Setter for property toReceiver.
     *
     * @param toReceiver
     *            New value of property toReceiver.
     */
    public void setToReceiver(java.lang.Thread toReceiver) {
        this.toReceiver = toReceiver;
    }
    
    /**
     * Getter for property keepReceiving.
     *
     * @return Value of property keepReceiving.
     */
    public boolean isKeepReceiving() {
        return keepReceiving;
    }
    
    /**
     * Setter for property keepReceiving.
     *
     * @param keepReceiving
     *            New value of property keepReceiving.
     */
    public void setKeepReceiving(boolean keepReceiving) {
        this.keepReceiving = keepReceiving;
    }
    
    /**
     * Getter for property receivedMessageStore.
     *
     * @return Value of property receivedMessageStore.
     */
    public guru.mercurio.eps.sSMPP.DataHandler getDataHandler() {
        return dataHandler;
    }
    
    /**
     * Setter for property receivedMessageStore.
     *
     * @param receivedMessageStore
     *            New value of property receivedMessageStore.
     */
    public void setDataHandler(guru.mercurio.eps.sSMPP.DataHandler dataHandler) {
        this.dataHandler = dataHandler;
    }
    
    /**
     * Getter for property messageHandler.
     *
     * @return Value of property messageHandler.
     */
    public guru.mercurio.eps.sSMPP.ReceivedMessageHandler getMessageHandler() {
        return messageHandler;
    }
    
    /**
     * Setter for property messageHandler.
     *
     * @param messageHandler
     *            New value of property messageHandler.
     */
    public void setMessageHandler(guru.mercurio.eps.sSMPP.ReceivedMessageHandler messageHandler) {
        this.messageHandler = messageHandler;
    }
    
    /**
     * Returns the bindType.
     *
     * @return String
     */
    public String getBindType() {
        return bindType;
    }
    
    /**
     * Sets the bindType.
     *
     * @param bindType
     *            The bindType to set
     */
    public void setBindType(String bindType) {
        this.bindType = bindType;
    }
    
    public Session getSession(){
        return this.session;
    }
    
    /*
     * AGREGADO TEMPORALMENTE POR SANTIAGO. MIENTRAS LA ACTUALIZACION DE LOS ESTADOS DE ERROR EN LOS
     * MDR SE HA MANEJADO POR EL BEAN
     */
    
    public static String driver;
    public static String dbmsURL;
    public static String username;
    public static String passworddb;
    
    public long getEnquireLinkTime() {
        return enquireLinkTime;
    }
    
    public void setEnquireLinkTime(long enquireLinkTime) {
        this.enquireLinkTime = enquireLinkTime;
    }
    
    
} // FIN DE LA CLASE SMPPOperator
